﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.ReflectionModel;
using System.IdentityModel.Selectors;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Caching;
using System.Runtime.Remoting.Messaging;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Security;
using System.Text;
using System.Threading;
using Kugar.Core.BaseStruct;
using Kugar.Core.Data.MSSQLHelper;
using Kugar.Core.Exceptions;
using Kugar.Core.ExtMethod;
using Kugar.ERPFramework.BLLInterface;
using Kugar.ERPFramework.BLLInterface.Session;
using Kugar.ERPFramework.Core.Entity;
using Kugar.ERPFramework.Core.Interfaces;
using System.ServiceModel;
using Kugar.Core.Log;

namespace Kugar.ERPFramework.Service
{
    public class ProjectInstance : IProjectInstance
    {
        [ImportMany(typeof (ICustomBLLModule), RequiredCreationPolicy = CreationPolicy.Shared)] private
            IEnumerable<Lazy<ICustomBLLModule, Dictionary<string, object>>> _importModule = null;

        private string _instanceID;

        private ServiceHost _host = null;

        [ThreadStatic] private static object _lockerObj = new object();

        private Dictionary<string, ThreadLocal<SQLDataHelper>> _cacheOperationConnection = null;

        private Dictionary<string, SQLHelperPool> _cachePool =
            new Dictionary<string, SQLHelperPool>(StringComparer.CurrentCultureIgnoreCase);

        private CompositionContainer _container = null;

        [ThreadStatic] private static volatile bool _isSubscribeConnectionEvent = false;

        public ProjectInstance(string instanceName, IProject project, string instanceID)
        {
            Name = instanceName;
            _instanceID = instanceID;
            Project = project;
            _cacheOperationConnection =
                new Dictionary<string, ThreadLocal<SQLDataHelper>>(StringComparer.CurrentCultureIgnoreCase);

            _host.Opened += host_Opened;

            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new DirectoryCatalog(project.Info.ProjectPath));
            catalog.Catalogs.Add(new AssemblyCatalog(typeof (ProjectInstance).Assembly));

            try
            {
                _container = new CompositionContainer(catalog);

                _container.ComposeExportedValue(this);

                _container.ComposeParts(this);
            }
            catch (Exception ex)
            {
                LoggerManager.CoreLogger.Error(ex.Message);
                throw;
            }

            var moduleTypeList = new List<Type>();
            var infoList = new List<InstanceModuleInfo>(moduleTypeList.Count);
            foreach (var lazy in _importModule)
            {
                var type = (Type) lazy.Metadata["ExportInterface"];

                if (!type.IsInterface //如果导出类型不是接口
                    || moduleTypeList.Any(x => x.Name == lazy.Value.Name) //或者接口名称重复,则跳过,,这里的名称,是 接口名,不包括命名空间
                    )
                {
                    continue;
                }

                moduleTypeList.Add(type);

                var info = new InstanceModuleInfo();
                info.Address = string.Format("net.tcp://127.0.0.1/Instance/{0}/{1}", instanceID, lazy.Value.Name);
                info.ModuleName = lazy.Value.Name;

                infoList.Add(info);

                var tcpBinding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
                tcpBinding.PortSharingEnabled = true;
                tcpBinding.SendTimeout = new TimeSpan(0, 1, 0);

                tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
                tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

                _host.AddServiceEndpoint(type, tcpBinding, Uri.EscapeUriString(info.Address));
            }

            var newDynamicType = build(moduleTypeList.ToArray());

            _host = new ServiceHost(Activator.CreateInstance(newDynamicType),
                                    new Uri("net.tcp://127.0.0.1/ProductInstance/"));

            _host.Description.Behaviors.Add(new InstanceProviderBehavior(this));

            _host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode =
                UserNamePasswordValidationMode.Custom;
            _host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UserPasswordValidator();

            _host.Credentials.ClientCertificate.Authentication.CertificateValidationMode =
                X509CertificateValidationMode.None;

            //TODO:读取配置,初始化连接
            initSQLPool();


            _host.Open();



        }

        public string Name { private set; get; }

        public void OnComplete()
        {

        }


        #region Implementation of IProjectInstance

        public ICustomBLLModule GetModuleByName(string moduleName)
        {
            var m= _importModule.FirstOrDefault(x => x.Metadata["ModuleName"] == moduleName);

            if (m!=null)
            {
                return m.Value;
            }
            else
            {
                throw new ArgumentOutOfRangeException("moduleName");
            }
        }

        public T GetModuleByName<T>(string moduleName) where T : ICustomBLLModule
        {
            var m = GetModuleByName(moduleName);

            return (T) m;
        }


        public object GetModuleByType(Type type)
        {
            if (!type.IsInterface)
            {
                throw new ArgumentTypeNotMatchException("type","Interface");
            }

            var m = _importModule.FirstOrDefault(x => (Type)x.Metadata["ExportType"] == type);

            if (m != null)
            {
                return m.Value;
            }
            else
            {
                throw new ArgumentOutOfRangeException("type");
            }
        }

        public T GetModule<T>() where T : ICustomBLLModule
        {
            return (T) GetModuleByType(typeof (T));
        }

        public ProjectInstanceInfo InstanceInfo { get; private set; }

        public ResultReturn Execute(string moduleName, string methodID, params object[] paramList)
        {
            var module = GetModuleByName(moduleName);

            if (module==null)
            {
                throw new ArgumentOutOfRangeException("moduleName");
            }

            return module.Execute(methodID, paramList);
        }

        public SQLDataHelper CurrentConnection
        {
            get
            {
                return GetOtherConnection();
                //var conn = (SQLDataHelper) CallContext.GetData("Conn_CurrentConnection");
                //if (conn==null)
                //{
                //    lock (_lockerObj)
                //    {
                //        conn = getCurrentConnection();
                //        CallContext.SetData("Conn_CurrentConnection", conn);
                //        OperationContext.Current.OperationCompleted += Current_OperationCompleted;
                //    }
                //}

                //return conn;
            }
        }


        public SQLDataHelper GetOtherConnection(string connectionName = "MainConnection")
        {
            if (!_isSubscribeConnectionEvent)
            {
                lock (_lockerObj)
                {
                    if (!_isSubscribeConnectionEvent)
                    {
                       OperationContext.Current.OperationCompleted+=Current_OperationCompleted;
                       _isSubscribeConnectionEvent = true;                        
                    }
 
                }
            }

            var c = _cacheOperationConnection.TryGetValue(connectionName);

            if (c==null)
            {
                throw new ArgumentOutOfRangeException("connectionName","不存在指定名称的链接");
            }

            return c.Value;
        }

        public SessionItem Session
        {
            get { return CustomSession.Default.CurrentSession; }
        }

        public IProject Project { get; private set; }

        public void Close()
        {
            _host.Close();

            foreach (var pool in _cachePool)
            {
                pool.Value.Dispose();
            }
        }

        #endregion


        private Type build(Type[] typeInterfaces)
        {
            var currentAppDomain = AppDomain.CurrentDomain;
            var assyName = new AssemblyName();
            assyName.Name = "DynamicAssemblyForServiceType";
            var assyBuilder =currentAppDomain.DefineDynamicAssembly(assyName, AssemblyBuilderAccess.Run);

            ModuleBuilder modBuilder =assyBuilder.DefineDynamicModule("DynamicModuleForServiceType");

            String newTypeName = "DynamicServiceType";
            TypeAttributes newTypeAttribute =TypeAttributes.Class | TypeAttributes.Public;
            //在这里把注册的业务模块契约列出来

            TypeBuilder typeBuilder = modBuilder.DefineType(newTypeName,newTypeAttribute, null, typeInterfaces);
            //实现接口
            foreach (Type baseInterface in typeInterfaces)
            {
                MethodInfo[] targetMethods = baseInterface.GetMethods();
                foreach (MethodInfo targetMethod in targetMethods)
                {
                    if (targetMethod.IsVirtual)
                    {
                        var paramType = targetMethod.GetParameters().Select(x => x.ParameterType).ToArray();

                        //ParameterInfo[] paramInfo = targetMethod.GetParameters();
                        //Type[] paramType = new Type[paramInfo.Length];
                        //for (int i = 0; i < paramInfo.Length; i++)
                        //{
                        //    paramType[i] = paramInfo[i].ParameterType;
                        //}
                        MethodBuilder methodBuilder = typeBuilder.DefineMethod(
                            targetMethod.Name,
                            MethodAttributes.Public | MethodAttributes.Virtual,
                            targetMethod.ReturnType, paramType);
                        ILGenerator ilGen = methodBuilder.GetILGenerator();
                        ilGen.Emit(OpCodes.Ret);   //该方法具体应该实现什么功能不重要.
                    }
                }
            }
            return (typeBuilder.CreateType());
        }

        private void Current_OperationCompleted(object sender, EventArgs e)
        {
            OperationContext.Current.OperationCompleted -= Current_OperationCompleted;

            foreach (var local in _cacheOperationConnection)
            {
                if (local.Value.IsValueCreated)
                {
                    local.Value.Dispose();
                }
            }
        }

        private void host_Opened(object sender, EventArgs e)
        {

        }

        private void initSQLPool()
        {
            var config = Project.GetConnectionByInstanceID(_instanceID);

            if (config == null || config.Length <= 0)
            {
                return;
            }

            foreach (var pair in config)
            {
                _cachePool.Add(pair.Name,new SQLHelperPool(pair.ConnectionString,100,5));
                _cacheOperationConnection.Add(pair.Name, new ThreadLocal<SQLDataHelper>(() => (SQLDataHelper)_cachePool.TryGetValue(pair.ConnectionString).Take()));
            }
        }
    }


    internal class MyInstanceProvicer : IInstanceProvider
    {
        private IProjectInstance _projectInstance;
        private string contractType;
        public MyInstanceProvicer(IProjectInstance projectInstance, string contractType)
        {
            this.contractType = contractType;
            _projectInstance = projectInstance;
        }

        public object GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
        {
            /*在注册业务模块的时候,可以把实现业务模块的assembly,业务模块的type name, 
            * 业务模块所实现的契约接口记录到一张表里,然后在这里通过契约找到对应的业 
            * 务模块信息,实例化出类实例,返回给servicehost*/

            return _projectInstance.GetModuleByName(contractType);

            //switch (contractType)
            //{
            //    case "IService1":
            //        return new Service1();
            //        break;
            //    case "IService2":
            //        return new Service2();
            //        break;
            //    default:
            //        return null;
            //        break;
            //}
            //return null;
        }

        public object GetInstance(InstanceContext instanceContext)
        {
            return GetInstance(instanceContext, null);
        }

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            //Console.WriteLine("releaseInstance::" + instance.ToString());
        }
    }

    internal class InstanceProviderBehavior : IServiceBehavior
    {
        private IProjectInstance _instance;
        public InstanceProviderBehavior(IProjectInstance instance)
        {
            _instance = instance;
        }

        void IServiceBehavior.AddBindingParameters(
        ServiceDescription serviceDescription,
        ServiceHostBase serviceHostBase,
        System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,
        System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher channelDispatcher
            in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    endpointDispatcher.DispatchRuntime.InstanceProvider = new MyInstanceProvicer(_instance,endpointDispatcher.ContractName);
                }
            }
        }

        void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
    }

    internal class UserPasswordValidator:UserNamePasswordValidator
    {
        #region Overrides of UserNamePasswordValidator

        public override void Validate(string userName, string password)
        {
            if (!Kugar.Core.Security.RMS_PermissionManager.Default.CheckUser(userName, password))
            {
                throw new FaultException("用户名密码错误,请重新登录");
            }
        }

        #endregion
    }

    public class InstanceModuleInfo
    {
        public string ModuleName { set; get; }
        public string Address { set; get; }
        public string TypeName { set; get; }
    }

}
